﻿/*
 * Copyright 2016 tian xianglong. All rights reserved.
 * 场景渲染
 */

/*
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/

#ifndef UNICODE
#define UNICODE
#endif

#ifndef _UNICODE
#define _UNICODE
#endif

#define _CRT_SECURE_NO_WARNINGS

#include <Windows.h>
#include <windowsx.h>

#include <d3d9.h>
#include <d3dx9.h>

#include <string.h>
#include <stdio.h>

#include "glyph.h"
#include "object.h"

#include "scene.h"

/*
	一个场景需要实现三个函数：
	OnDrawScene ： 渲染整个场景
	OnLostDevice ： 释放所有资源
	OnResetDevice ： 重新创建所有资源
	OnInitScene：初始化整个场景
	OnExitScene：场景要销毁前被调用
 */

 /*
	坐标系描述

	D3D 投影坐标系：

	投影：在相机坐标系中，把视椎体中的物体投影到Z=z平面上，这个过程叫投影，这个平面叫投影面。

	在投影面上建立坐标系，称做投影坐标系，定义如下
		原点：相机坐标系的Z轴与投影面的交点
		x轴：与相机坐标系的x轴同向
		y轴：与相机坐标系的y轴同向
		z轴：与相机坐标系的z轴同向

	RenderTarget
	可以用于存储渲染结果的二维数组，D3D中称作Surface,数组元素是像素

	RenderTarget坐标系
	RenderTarget实质上是一个IDirect3DSurface9对象，它的坐标系定义与Windows窗口坐标系相同

	X轴，向右延伸
	Y轴，向下延伸
	左上角是(0, 0)
	右下角是(Width, Height)

 */

void OnLostDevice()
{

}

void OnResetDevice()
{

}

void GetResourceFilePath(wchar_t *filepath, wchar_t *filename)
{
	wchar_t *p1 = 0;
	GetModuleFileNameW(NULL, filepath, MAX_PATH);
	if (p1 = wcsrchr(filepath, L'\\') + 1)
		wcscpy(p1, filename);
	else
		wcscpy(filepath, filename);
}

static LPDIRECT3DTEXTURE9 textures[1] = { NULL };
static int texcount = 0;

void AddTexture(const wchar_t *lpszPicture)
{
	if (texcount >= sizeof(textures) / sizeof(textures[0]))
		return;

	LPDIRECT3DTEXTURE9 pTexture = 0;

	HRESULT hr;
	//hr = D3DXCreateTextureFromFileW(g_lpDevice9, lpszPicture, &pTexture);

	D3DXIMAGE_INFO info;

	hr = D3DXCreateTextureFromFileExW(
		g_lpDevice9, 
		lpszPicture, 
		D3DX_DEFAULT_NONPOW2,
		D3DX_DEFAULT_NONPOW2,
		1,
		0, 
		D3DFMT_UNKNOWN, 
		D3DPOOL_MANAGED, 
		D3DX_DEFAULT,
		D3DX_DEFAULT,
		0,
		&info,
		NULL, 
		&pTexture
		);

	UINT c = IDirect3DTexture9_GetLevelCount(pTexture);

	D3DSURFACE_DESC desc;
	IDirect3DTexture9_GetLevelDesc(pTexture, 0, &desc);

	if (hr != D3D_OK)
	{
		wchar_t buf[100] = { 0 };
		swprintf(buf, sizeof(buf), L"\"%s\"不存在，或格式错误，%04d", lpszPicture, (int)hr);
		MessageBoxW(g_hMainWindow, buf, L"纹理创建失败", MB_OK);
		return;
	}

	textures[texcount++] = pTexture;
}

void OnInitScene()
{
	wchar_t szPicture[MAX_PATH] = { 0 };

	GetResourceFilePath(szPicture, L"2.bmp");
	AddTexture(szPicture);

	GetResourceFilePath(szPicture, L"1.png");
	AddTexture(szPicture);
}

void OnExitScene()
{
	while (texcount-- > 0)
	{
		IDirect3DTexture9_Release(textures[texcount]);
	}
}

void OnDrawScene()
{
	HRESULT hr;

	/* 首先，清屏 */
	hr = IDirect3DDevice9_Clear(g_lpDevice9,
		0,
		NULL,
		D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER,
		//D3DCOLOR_ARGB(255, 214, 214, 214),
		D3DCOLOR_ARGB(255, 255, 255, 255),
		1.0f,
		0);


	hr = IDirect3DDevice9_BeginScene(g_lpDevice9);

	/* 画一个三角形 */
	draw_a_triangle();

	draw_a_picture();

	hr = IDirect3DDevice9_EndScene(g_lpDevice9);

	/* 最后，呈现场景 */
	hr = IDirect3DDevice9_Present(g_lpDevice9, NULL, NULL, NULL, NULL);
}

/*
如何显示图片？
首先要明白，在三维渲染中，能够绘制的基本元素是顶点（由位置、法向、颜色、纹理坐标等属性组成）
因此在三维空间中显示一张图片，首先得构造四个顶点，分别对应图片的四个角，并给四个顶点
分配合适的纹理坐标。

纹理：是显卡用来管理图片的一种资源类型，常用的有 *.dds、*.tga、*.bmp、*.png
顶点：是显卡能够处理的基本数据类型，如果想用用C语言的struct表示顶点的话，可以参考：Baidu "Flexible vertex format bits"
图元：是显卡能够绘制的几何类型，常用的有：点、线段、三角形

Ok.
开始显示图片流程：
1、使用D3DXCreateTextureFromFile，创建纹理
2、定义包含4个顶点的数组，每个顶点包含：位置、纹理坐标
3、初始化顶点数组，将四个顶点组织成两个三角形，比如用主对角线分割成两个三角形
4、激活纹理，绘制两个三角形
 */

struct float4
{
	float left;
	float top;
	float right;
	float bottom;
};

typedef struct float4 float4_t;

static const DWORD VertexFVF = D3DFVF_XYZRHW | D3DFVF_TEX1 | D3DFVF_TEXCOORDSIZE2(0);
struct VERTEX
{
	FLOAT	x, y, z, rhw;
	FLOAT	u, v;
};

void AlignToCell(struct VERTEX *vertices, size_t count)
{
	size_t c = 0;
	for (c = 0; c < count; ++c)
	{
		vertices[c].x -= 0.5f;
		vertices[c].y -= 0.5f;
	}
}

/*
	lpRect：屏幕上的矩形
	lpTexture：图片的纹理对象
 */
void DrawPicture(float4_t *lpRect, LPDIRECT3DTEXTURE9 lpTexture, float4_t *lpTexCoord)
{
	HRESULT hr;

	float left = lpRect->left;
	float top = lpRect->top;
	float right = lpRect->right;
	float bottom = lpRect->bottom;

	float tex_left = lpTexCoord->left;
	float tex_top = lpTexCoord->top;
	float tex_right = lpTexCoord->right;
	float tex_bottom = lpTexCoord->bottom;

	struct VERTEX pt[] = {
		/* 第一个三角形 */
		{ left, top, 0.0f, 1.0f, tex_left, tex_top },
		{ right, top, 0.0f, 1.0f, tex_right, tex_top },
		{ left, bottom, 0.0f, 1.0f, tex_left, tex_bottom },

		/* 第二个三角形 */
		{ left, bottom, 0.0f, 1.0f, tex_left, tex_bottom },
		{ right, top, 0.0f, 1.0f, tex_right, tex_top },
		{ right, bottom, 0.0f, 1.0f, tex_right, tex_bottom },
	};

	AlignToCell(pt, sizeof(pt) / sizeof(pt[0]));

	hr = IDirect3DDevice9_SetFVF(g_lpDevice9, VertexFVF);

	hr = IDirect3DDevice9_SetTexture(g_lpDevice9, 0, (LPDIRECT3DBASETEXTURE9)lpTexture);
	
	hr = IDirect3DDevice9_SetSamplerState(g_lpDevice9, 0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
	hr = IDirect3DDevice9_SetSamplerState(g_lpDevice9, 0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
	hr = IDirect3DDevice9_SetSamplerState(g_lpDevice9, 0, D3DSAMP_MIPFILTER, D3DTEXF_NONE);

	IDirect3DDevice9_SetRenderState(g_lpDevice9, D3DRS_ALPHABLENDENABLE, FALSE);
	IDirect3DDevice9_SetRenderState(g_lpDevice9, D3DRS_ALPHATESTENABLE, FALSE);
	
	hr = IDirect3DDevice9_DrawPrimitiveUP(
		g_lpDevice9, 
		D3DPT_TRIANGLELIST, 
		2, 
		&pt[0], 
		sizeof(pt[0])
	);

	hr = IDirect3DDevice9_SetTexture(g_lpDevice9, 0, NULL);
}

void draw_a_picture()
{
	float4_t rc = { 0.0f, 0.0f, 270.0f, 374.0f };
	float4_t texc = { 0.0f, 0.0f, 1.0f, 1.0f };

	LPDIRECT3DTEXTURE9 lpTexture = texcount > 0 ? textures[0] : NULL;
	DrawPicture(&rc, lpTexture, &texc);
}

void draw_a_triangle()
{
	HRESULT hr;

	const DWORD VertexFVF = (D3DFVF_XYZ | D3DFVF_DIFFUSE);
	struct Vertex
	{
		FLOAT    x, y, z;
		D3DCOLOR diffuse;
	};

	struct Vertex pt[6] = {
		/* 第一个三角形 */
		{ 0.0f, 0.0f, 0.0f, 0xffaa0000 },
		{ 0.0f, -1.0f, 0.0f, 0xff00ff00 },
		{ 1.0f, 0.0f, 0.0f, 0xff0000ff },

		/* 第二个三角形 */
		{ 0.0f, 0.0f, 1.0f, 0xff0000ff },
		{ -1.0f, -1.0f, 1.0f, 0xff0000ff },
		{ 1.0f, -1.0f, 1.0f, 0xff0000ff },
	};

	/* 设置顶点格式 */
	hr = IDirect3DDevice9_SetFVF(g_lpDevice9, VertexFVF);

	/* 绘制图元 */
	hr = IDirect3DDevice9_DrawPrimitiveUP(
		g_lpDevice9, 
		D3DPT_TRIANGLELIST, 
		sizeof(pt) / sizeof(pt[0]) / 3,
		&pt[0], 
		sizeof(pt[0])
	);
}
